// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:shared_preferences_android/shared_preferences_android.dart';
import 'package:shared_preferences_android/src/messages_async.g.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';
import 'package:shared_preferences_platform_interface/types.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('SharedPreferencesAndroid', () {
    const flutterTestValues = <String, Object>{
      'flutter.String': 'hello world',
      'flutter.Bool': true,
      'flutter.Int': 42,
      'flutter.Double': 3.14159,
      'flutter.StringList': <String>['foo', 'bar'],
    };

    const prefixTestValues = <String, Object>{
      'prefix.String': 'hello world',
      'prefix.Bool': true,
      'prefix.Int': 42,
      'prefix.Double': 3.14159,
      'prefix.StringList': <String>['foo', 'bar'],
    };

    const nonPrefixTestValues = <String, Object>{
      'String': 'hello world',
      'Bool': true,
      'Int': 42,
      'Double': 3.14159,
      'StringList': <String>['foo', 'bar'],
    };

    final allTestValues = <String, Object>{};

    allTestValues.addAll(flutterTestValues);
    allTestValues.addAll(prefixTestValues);
    allTestValues.addAll(nonPrefixTestValues);

    late SharedPreferencesStorePlatform preferences;

    setUp(() async {
      preferences = SharedPreferencesStorePlatform.instance;
    });

    tearDown(() async {
      await preferences.clearWithParameters(
        ClearParameters(filter: PreferencesFilter(prefix: '')),
      );
    });

    testWidgets('reading', (WidgetTester _) async {
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: '')),
      );
      expect(values['String'], isNull);
      expect(values['Bool'], isNull);
      expect(values['Int'], isNull);
      expect(values['Double'], isNull);
      expect(values['StringList'], isNull);
    });

    Future<void> addData() async {
      await preferences.setValue('String', 'String', allTestValues['String']!);
      await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!);
      await preferences.setValue('Int', 'Int', allTestValues['Int']!);
      await preferences.setValue('Double', 'Double', allTestValues['Double']!);
      await preferences.setValue(
        'StringList',
        'StringList',
        allTestValues['StringList']!,
      );
      await preferences.setValue(
        'String',
        'prefix.String',
        allTestValues['prefix.String']!,
      );
      await preferences.setValue(
        'Bool',
        'prefix.Bool',
        allTestValues['prefix.Bool']!,
      );
      await preferences.setValue(
        'Int',
        'prefix.Int',
        allTestValues['prefix.Int']!,
      );
      await preferences.setValue(
        'Double',
        'prefix.Double',
        allTestValues['prefix.Double']!,
      );
      await preferences.setValue(
        'StringList',
        'prefix.StringList',
        allTestValues['prefix.StringList']!,
      );
      await preferences.setValue(
        'String',
        'flutter.String',
        allTestValues['flutter.String']!,
      );
      await preferences.setValue(
        'Bool',
        'flutter.Bool',
        allTestValues['flutter.Bool']!,
      );
      await preferences.setValue(
        'Int',
        'flutter.Int',
        allTestValues['flutter.Int']!,
      );
      await preferences.setValue(
        'Double',
        'flutter.Double',
        allTestValues['flutter.Double']!,
      );
      await preferences.setValue(
        'StringList',
        'flutter.StringList',
        allTestValues['flutter.StringList']!,
      );
    }

    testWidgets('getAllWithPrefix', (WidgetTester _) async {
      await Future.wait(<Future<bool>>[
        preferences.setValue(
          'String',
          'prefix.String',
          allTestValues['prefix.String']!,
        ),
        preferences.setValue(
          'Bool',
          'prefix.Bool',
          allTestValues['prefix.Bool']!,
        ),
        preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!),
        preferences.setValue(
          'Double',
          'prefix.Double',
          allTestValues['prefix.Double']!,
        ),
        preferences.setValue(
          'StringList',
          'prefix.StringList',
          allTestValues['prefix.StringList']!,
        ),
        preferences.setValue(
          'String',
          'flutter.String',
          allTestValues['flutter.String']!,
        ),
        preferences.setValue(
          'Bool',
          'flutter.Bool',
          allTestValues['flutter.Bool']!,
        ),
        preferences.setValue(
          'Int',
          'flutter.Int',
          allTestValues['flutter.Int']!,
        ),
        preferences.setValue(
          'Double',
          'flutter.Double',
          allTestValues['flutter.Double']!,
        ),
        preferences.setValue(
          'StringList',
          'flutter.StringList',
          allTestValues['flutter.StringList']!,
        ),
      ]);
      final Map<String, Object> values =
          // ignore: deprecated_member_use
          await preferences.getAllWithPrefix('prefix.');
      expect(values['prefix.String'], allTestValues['prefix.String']);
      expect(values['prefix.Bool'], allTestValues['prefix.Bool']);
      expect(values['prefix.Int'], allTestValues['prefix.Int']);
      expect(values['prefix.Double'], allTestValues['prefix.Double']);
      expect(values['prefix.StringList'], allTestValues['prefix.StringList']);
    });

    group('withPrefix', () {
      testWidgets('clearWithPrefix', (WidgetTester _) async {
        await Future.wait(<Future<bool>>[
          preferences.setValue(
            'String',
            'prefix.String',
            allTestValues['prefix.String']!,
          ),
          preferences.setValue(
            'Bool',
            'prefix.Bool',
            allTestValues['prefix.Bool']!,
          ),
          preferences.setValue(
            'Int',
            'prefix.Int',
            allTestValues['prefix.Int']!,
          ),
          preferences.setValue(
            'Double',
            'prefix.Double',
            allTestValues['prefix.Double']!,
          ),
          preferences.setValue(
            'StringList',
            'prefix.StringList',
            allTestValues['prefix.StringList']!,
          ),
          preferences.setValue(
            'String',
            'flutter.String',
            allTestValues['flutter.String']!,
          ),
          preferences.setValue(
            'Bool',
            'flutter.Bool',
            allTestValues['flutter.Bool']!,
          ),
          preferences.setValue(
            'Int',
            'flutter.Int',
            allTestValues['flutter.Int']!,
          ),
          preferences.setValue(
            'Double',
            'flutter.Double',
            allTestValues['flutter.Double']!,
          ),
          preferences.setValue(
            'StringList',
            'flutter.StringList',
            allTestValues['flutter.StringList']!,
          ),
        ]);
        // ignore: deprecated_member_use
        await preferences.clearWithPrefix('prefix.');
        Map<String, Object> values =
            // ignore: deprecated_member_use
            await preferences.getAllWithPrefix('prefix.');
        expect(values['prefix.String'], null);
        expect(values['prefix.Bool'], null);
        expect(values['prefix.Int'], null);
        expect(values['prefix.Double'], null);
        expect(values['prefix.StringList'], null);
        // ignore: deprecated_member_use
        values = await preferences.getAllWithPrefix('flutter.');
        expect(values['flutter.String'], allTestValues['flutter.String']);
        expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
        expect(values['flutter.Int'], allTestValues['flutter.Int']);
        expect(values['flutter.Double'], allTestValues['flutter.Double']);
        expect(
          values['flutter.StringList'],
          allTestValues['flutter.StringList'],
        );
      });

      testWidgets('getAllWithNoPrefix', (WidgetTester _) async {
        await Future.wait(<Future<bool>>[
          preferences.setValue('String', 'String', allTestValues['String']!),
          preferences.setValue('Bool', 'Bool', allTestValues['Bool']!),
          preferences.setValue('Int', 'Int', allTestValues['Int']!),
          preferences.setValue('Double', 'Double', allTestValues['Double']!),
          preferences.setValue(
            'StringList',
            'StringList',
            allTestValues['StringList']!,
          ),
          preferences.setValue(
            'String',
            'flutter.String',
            allTestValues['flutter.String']!,
          ),
          preferences.setValue(
            'Bool',
            'flutter.Bool',
            allTestValues['flutter.Bool']!,
          ),
          preferences.setValue(
            'Int',
            'flutter.Int',
            allTestValues['flutter.Int']!,
          ),
          preferences.setValue(
            'Double',
            'flutter.Double',
            allTestValues['flutter.Double']!,
          ),
          preferences.setValue(
            'StringList',
            'flutter.StringList',
            allTestValues['flutter.StringList']!,
          ),
        ]);
        final Map<String, Object> values =
            // ignore: deprecated_member_use
            await preferences.getAllWithPrefix('');
        expect(values['String'], allTestValues['String']);
        expect(values['Bool'], allTestValues['Bool']);
        expect(values['Int'], allTestValues['Int']);
        expect(values['Double'], allTestValues['Double']);
        expect(values['StringList'], allTestValues['StringList']);
        expect(values['flutter.String'], allTestValues['flutter.String']);
        expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
        expect(values['flutter.Int'], allTestValues['flutter.Int']);
        expect(values['flutter.Double'], allTestValues['flutter.Double']);
        expect(
          values['flutter.StringList'],
          allTestValues['flutter.StringList'],
        );
      });

      testWidgets('clearWithNoPrefix', (WidgetTester _) async {
        await Future.wait(<Future<bool>>[
          preferences.setValue('String', 'String', allTestValues['String']!),
          preferences.setValue('Bool', 'Bool', allTestValues['Bool']!),
          preferences.setValue('Int', 'Int', allTestValues['Int']!),
          preferences.setValue('Double', 'Double', allTestValues['Double']!),
          preferences.setValue(
            'StringList',
            'StringList',
            allTestValues['StringList']!,
          ),
          preferences.setValue(
            'String',
            'flutter.String',
            allTestValues['flutter.String']!,
          ),
          preferences.setValue(
            'Bool',
            'flutter.Bool',
            allTestValues['flutter.Bool']!,
          ),
          preferences.setValue(
            'Int',
            'flutter.Int',
            allTestValues['flutter.Int']!,
          ),
          preferences.setValue(
            'Double',
            'flutter.Double',
            allTestValues['flutter.Double']!,
          ),
          preferences.setValue(
            'StringList',
            'flutter.StringList',
            allTestValues['flutter.StringList']!,
          ),
        ]);
        // ignore: deprecated_member_use
        await preferences.clearWithPrefix('');
        final Map<String, Object> values =
            // ignore: deprecated_member_use
            await preferences.getAllWithPrefix('');
        expect(values['String'], null);
        expect(values['Bool'], null);
        expect(values['Int'], null);
        expect(values['Double'], null);
        expect(values['StringList'], null);
        expect(values['flutter.String'], null);
        expect(values['flutter.Bool'], null);
        expect(values['flutter.Int'], null);
        expect(values['flutter.Double'], null);
        expect(values['flutter.StringList'], null);
      });
    });

    testWidgets('get all with prefix', (WidgetTester _) async {
      await addData();
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: 'prefix.')),
      );
      expect(values['prefix.String'], allTestValues['prefix.String']);
      expect(values['prefix.Bool'], allTestValues['prefix.Bool']);
      expect(values['prefix.Int'], allTestValues['prefix.Int']);
      expect(values['prefix.Double'], allTestValues['prefix.Double']);
      expect(values['prefix.StringList'], allTestValues['prefix.StringList']);
    });

    testWidgets('get all with allow list', (WidgetTester _) async {
      await addData();
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(
          filter: PreferencesFilter(
            prefix: 'prefix.',
            allowList: <String>{'prefix.String'},
          ),
        ),
      );
      expect(values['prefix.String'], allTestValues['prefix.String']);
      expect(values['prefix.Bool'], null);
      expect(values['prefix.Int'], null);
      expect(values['prefix.Double'], null);
      expect(values['prefix.StringList'], null);
    });

    testWidgets('getAllWithNoPrefix', (WidgetTester _) async {
      await addData();
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: '')),
      );
      expect(values['String'], allTestValues['String']);
      expect(values['Bool'], allTestValues['Bool']);
      expect(values['Int'], allTestValues['Int']);
      expect(values['Double'], allTestValues['Double']);
      expect(values['StringList'], allTestValues['StringList']);
      expect(values['flutter.String'], allTestValues['flutter.String']);
      expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
      expect(values['flutter.Int'], allTestValues['flutter.Int']);
      expect(values['flutter.Double'], allTestValues['flutter.Double']);
      expect(values['flutter.StringList'], allTestValues['flutter.StringList']);
    });

    testWidgets('clearWithParameters', (WidgetTester _) async {
      await addData();
      await preferences.clearWithParameters(
        ClearParameters(filter: PreferencesFilter(prefix: 'prefix.')),
      );
      Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: 'prefix.')),
      );
      expect(values['prefix.String'], null);
      expect(values['prefix.Bool'], null);
      expect(values['prefix.Int'], null);
      expect(values['prefix.Double'], null);
      expect(values['prefix.StringList'], null);
      values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: 'flutter.')),
      );
      expect(values['flutter.String'], allTestValues['flutter.String']);
      expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
      expect(values['flutter.Int'], allTestValues['flutter.Int']);
      expect(values['flutter.Double'], allTestValues['flutter.Double']);
      expect(values['flutter.StringList'], allTestValues['flutter.StringList']);
    });

    testWidgets('clearWithParameters with allow list', (WidgetTester _) async {
      await addData();
      await preferences.clearWithParameters(
        ClearParameters(
          filter: PreferencesFilter(
            prefix: 'prefix.',
            allowList: <String>{
              'prefix.Double',
              'prefix.Int',
              'prefix.Bool',
              'prefix.String',
            },
          ),
        ),
      );
      Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: 'prefix.')),
      );
      expect(values['prefix.String'], null);
      expect(values['prefix.Bool'], null);
      expect(values['prefix.Int'], null);
      expect(values['prefix.Double'], null);
      expect(values['prefix.StringList'], allTestValues['prefix.StringList']);
      values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: 'flutter.')),
      );
      expect(values['flutter.String'], allTestValues['flutter.String']);
      expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
      expect(values['flutter.Int'], allTestValues['flutter.Int']);
      expect(values['flutter.Double'], allTestValues['flutter.Double']);
      expect(values['flutter.StringList'], allTestValues['flutter.StringList']);
    });

    testWidgets('clearWithNoPrefix', (WidgetTester _) async {
      await addData();
      await preferences.clearWithParameters(
        ClearParameters(filter: PreferencesFilter(prefix: '')),
      );
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: '')),
      );
      expect(values['String'], null);
      expect(values['Bool'], null);
      expect(values['Int'], null);
      expect(values['Double'], null);
      expect(values['StringList'], null);
      expect(values['flutter.String'], null);
      expect(values['flutter.Bool'], null);
      expect(values['flutter.Int'], null);
      expect(values['flutter.Double'], null);
      expect(values['flutter.StringList'], null);
    });

    testWidgets('getAll', (WidgetTester _) async {
      await preferences.setValue(
        'String',
        'flutter.String',
        allTestValues['flutter.String']!,
      );
      await preferences.setValue(
        'Bool',
        'flutter.Bool',
        allTestValues['flutter.Bool']!,
      );
      await preferences.setValue(
        'Int',
        'flutter.Int',
        allTestValues['flutter.Int']!,
      );
      await preferences.setValue(
        'Double',
        'flutter.Double',
        allTestValues['flutter.Double']!,
      );
      await preferences.setValue(
        'StringList',
        'flutter.StringList',
        allTestValues['flutter.StringList']!,
      );
      final Map<String, Object> values = await preferences.getAll();
      expect(values['flutter.String'], allTestValues['flutter.String']);
      expect(values['flutter.Bool'], allTestValues['flutter.Bool']);
      expect(values['flutter.Int'], allTestValues['flutter.Int']);
      expect(values['flutter.Double'], allTestValues['flutter.Double']);
      expect(values['flutter.StringList'], allTestValues['flutter.StringList']);
    });

    testWidgets('remove', (WidgetTester _) async {
      const key = 'testKey';
      await preferences.setValue(
        'String',
        key,
        allTestValues['flutter.String']!,
      );
      await preferences.setValue('Bool', key, allTestValues['flutter.Bool']!);
      await preferences.setValue('Int', key, allTestValues['flutter.Int']!);
      await preferences.setValue(
        'Double',
        key,
        allTestValues['flutter.Double']!,
      );
      await preferences.setValue(
        'StringList',
        key,
        allTestValues['flutter.StringList']!,
      );
      await preferences.remove(key);
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: '')),
      );
      expect(values[key], isNull);
    });

    testWidgets('clear', (WidgetTester _) async {
      await preferences.setValue(
        'String',
        'flutter.String',
        allTestValues['flutter.String']!,
      );
      await preferences.setValue(
        'Bool',
        'flutter.Bool',
        allTestValues['flutter.Bool']!,
      );
      await preferences.setValue(
        'Int',
        'flutter.Int',
        allTestValues['flutter.Int']!,
      );
      await preferences.setValue(
        'Double',
        'flutter.Double',
        allTestValues['flutter.Double']!,
      );
      await preferences.setValue(
        'StringList',
        'flutter.StringList',
        allTestValues['flutter.StringList']!,
      );
      await preferences.clear();
      final Map<String, Object> values = await preferences.getAll();
      expect(values['flutter.String'], null);
      expect(values['flutter.Bool'], null);
      expect(values['flutter.Int'], null);
      expect(values['flutter.Double'], null);
      expect(values['flutter.StringList'], null);
    });

    testWidgets('simultaneous writes', (WidgetTester _) async {
      final writes = <Future<bool>>[];
      const writeCount = 100;
      for (var i = 1; i <= writeCount; i++) {
        writes.add(preferences.setValue('Int', 'Int', i));
      }
      final List<bool> result = await Future.wait(writes, eagerError: true);
      // All writes should succeed.
      expect(result.where((bool element) => !element), isEmpty);
      // The last write should win.
      final Map<String, Object> values = await preferences.getAllWithParameters(
        GetAllParameters(filter: PreferencesFilter(prefix: '')),
      );
      expect(values['Int'], writeCount);
    });

    testWidgets('string clash with lists and doubles', (WidgetTester _) async {
      const key = 'aKey';
      const value = 'a string value';
      await preferences.clearWithParameters(
        ClearParameters(filter: PreferencesFilter(prefix: '')),
      );

      // Special prefixes used to store datatypes that can't be stored directly
      // in SharedPreferences as strings instead.
      const specialPrefixes = <String>[
        // Prefix for lists:
        'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu',
        // Prefix for doubles:
        'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBEb3VibGUu',
      ];
      for (final prefix in specialPrefixes) {
        expect(
          preferences.setValue('String', key, prefix + value),
          throwsA(isA<PlatformException>()),
        );
        final Map<String, Object> values = await preferences
            .getAllWithParameters(
              GetAllParameters(filter: PreferencesFilter(prefix: '')),
            );
        expect(values[key], null);
      }
    });

    testWidgets(
      'Platform list encoding with getPreferences can be re-added with new encoding without data loss',
      (WidgetTester _) async {
        await preferences.clearWithParameters(
          ClearParameters(filter: PreferencesFilter(prefix: '')),
        );
        await preferences.setValue(
          'String',
          'String',
          allTestValues['String']!,
        );
        await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!);
        await preferences.setValue('Int', 'Int', allTestValues['Int']!);
        await preferences.setValue(
          'Double',
          'Double',
          allTestValues['Double']!,
        );
        await (preferences as SharedPreferencesAndroid).api
            .setDeprecatedStringList(
              'StringList',
              allTestValues['StringList']! as List<String>,
            );
        Map<String, Object> prefs = await preferences.getAllWithParameters(
          GetAllParameters(filter: PreferencesFilter(prefix: '')),
        );
        expect(prefs['StringList'], allTestValues['StringList']);
        await preferences.setValue(
          'StringList',
          'StringList',
          prefs['StringList']!,
        );
        prefs = await preferences.getAllWithParameters(
          GetAllParameters(filter: PreferencesFilter(prefix: '')),
        );

        expect(prefs['StringList'], allTestValues['StringList']);
      },
    );
  });

  const stringKey = 'testString';
  const boolKey = 'testBool';
  const intKey = 'testInt';
  const doubleKey = 'testDouble';
  const listKey = 'testList';

  const testString = 'hello world';
  const testBool = true;
  const testInt = 42;
  const testDouble = 3.14159;
  const testList = <String>['foo', 'bar'];

  SharedPreferencesAsyncAndroidOptions getOptions({
    required bool useDataStore,
    String? fileName,
  }) {
    return SharedPreferencesAsyncAndroidOptions(
      backend: useDataStore
          ? SharedPreferencesAndroidBackendLibrary.DataStore
          : SharedPreferencesAndroidBackendLibrary.SharedPreferences,
      originalSharedPreferencesOptions: fileName == null
          ? null
          : AndroidSharedPreferencesStoreOptions(fileName: fileName),
    );
  }

  Future<void> clearPreferences(
    SharedPreferencesAsyncPlatform preferences,
    SharedPreferencesAsyncAndroidOptions options,
  ) async {
    await preferences.clear(
      const ClearPreferencesParameters(filter: PreferencesFilters()),
      options,
    );
  }

  SharedPreferencesAsyncPlatform getPreferences() {
    final SharedPreferencesAsyncPlatform preferences =
        SharedPreferencesAsyncPlatform.instance!;
    return preferences;
  }

  void runAsyncTests(bool useDataStore) {
    group('shared_preferences_async', () {
      final backend = useDataStore ? 'DataStore' : 'SharedPreferences';

      testWidgets('set and get String with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setString(stringKey, testString, options);
        expect(await preferences.getString(stringKey, options), testString);
      });

      testWidgets('set and get bool with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setBool(boolKey, testBool, options);
        expect(await preferences.getBool(boolKey, options), testBool);
      });

      testWidgets('set and get int with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setInt(intKey, testInt, options);
        expect(await preferences.getInt(intKey, options), testInt);
      });

      testWidgets('set and get double with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setDouble(doubleKey, testDouble, options);
        expect(await preferences.getDouble(doubleKey, options), testDouble);
      });

      testWidgets('set and get StringList with $backend', (
        WidgetTester _,
      ) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setStringList(listKey, testList, options);
        expect(await preferences.getStringList(listKey, options), testList);
      });

      testWidgets('getStringList returns mutable list with $backend', (
        WidgetTester _,
      ) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setStringList(listKey, testList, options);
        final List<String>? list = await preferences.getStringList(
          listKey,
          options,
        );
        list?.add('value');
        expect(list?.length, testList.length + 1);
      });

      testWidgets('getStringList throws type error for String with $backend', (
        WidgetTester _,
      ) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);

        await preferences.setString(listKey, testString, options);

        // Internally, List<String> is stored as a String on Android, but that
        // implementation detail shouldn't leak to clients; getting the wrong
        // type should still throw.
        expect(
          preferences.getStringList(listKey, options),
          throwsA(isA<TypeError>()),
        );
      });

      testWidgets('getPreferences with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);

        final Map<String, Object?> gotAll = await preferences.getPreferences(
          const GetPreferencesParameters(filter: PreferencesFilters()),
          options,
        );

        expect(gotAll.length, 5);
        expect(gotAll[stringKey], testString);
        expect(gotAll[boolKey], testBool);
        expect(gotAll[intKey], testInt);
        expect(gotAll[doubleKey], testDouble);
        expect(gotAll[listKey], testList);
      });

      testWidgets('getPreferences with filter with $backend', (
        WidgetTester _,
      ) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);

        final Map<String, Object?> gotAll = await preferences.getPreferences(
          const GetPreferencesParameters(
            filter: PreferencesFilters(allowList: <String>{stringKey, boolKey}),
          ),
          options,
        );

        expect(gotAll.length, 2);
        expect(gotAll[stringKey], testString);
        expect(gotAll[boolKey], testBool);
      });

      testWidgets('getKeys with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);

        final Set<String> keys = await preferences.getKeys(
          const GetPreferencesParameters(filter: PreferencesFilters()),
          options,
        );

        expect(keys.length, 5);
        expect(keys, contains(stringKey));
        expect(keys, contains(boolKey));
        expect(keys, contains(intKey));
        expect(keys, contains(doubleKey));
        expect(keys, contains(listKey));
      });

      testWidgets('getKeys with filter with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);

        final Set<String> keys = await preferences.getKeys(
          const GetPreferencesParameters(
            filter: PreferencesFilters(allowList: <String>{stringKey, boolKey}),
          ),
          options,
        );

        expect(keys.length, 2);
        expect(keys, contains(stringKey));
        expect(keys, contains(boolKey));
      });

      testWidgets('clear with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);

        await preferences.clear(
          const ClearPreferencesParameters(filter: PreferencesFilters()),
          options,
        );

        expect(await preferences.getString(stringKey, options), null);
        expect(await preferences.getBool(boolKey, options), null);
        expect(await preferences.getInt(intKey, options), null);
        expect(await preferences.getDouble(doubleKey, options), null);
        expect(await preferences.getStringList(listKey, options), null);
      });

      testWidgets('clear with filter with $backend', (WidgetTester _) async {
        final SharedPreferencesAsyncAndroidOptions options = getOptions(
          useDataStore: useDataStore,
          fileName: 'notDefault',
        );
        final SharedPreferencesAsyncPlatform preferences = getPreferences();
        await clearPreferences(preferences, options);
        await Future.wait(<Future<void>>[
          preferences.setString(stringKey, testString, options),
          preferences.setBool(boolKey, testBool, options),
          preferences.setInt(intKey, testInt, options),
          preferences.setDouble(doubleKey, testDouble, options),
          preferences.setStringList(listKey, testList, options),
        ]);
        await preferences.clear(
          const ClearPreferencesParameters(
            filter: PreferencesFilters(allowList: <String>{stringKey, boolKey}),
          ),
          options,
        );
        expect(await preferences.getString(stringKey, options), null);
        expect(await preferences.getBool(boolKey, options), null);
        expect(await preferences.getInt(intKey, options), testInt);
        expect(await preferences.getDouble(doubleKey, options), testDouble);
        expect(await preferences.getStringList(listKey, options), testList);
      });

      testWidgets(
        'platform list encoding updates to JSON encoding process without data loss with $backend',
        (WidgetTester _) async {
          final SharedPreferencesAsyncAndroidOptions options = getOptions(
            useDataStore: useDataStore,
            fileName: 'notDefault',
          );
          final preferences = getPreferences() as SharedPreferencesAsyncAndroid;
          await clearPreferences(preferences, options);
          final SharedPreferencesPigeonOptions pigeonOptions = preferences
              .convertOptionsToPigeonOptions(options);
          final SharedPreferencesAsyncApi api = preferences.getApiForBackend(
            pigeonOptions,
          );
          await api.setDeprecatedStringList(listKey, testList, pigeonOptions);
          final List<String>? platformEncodedList = await preferences
              .getStringList(listKey, options);
          expect(platformEncodedList, testList);
          await preferences.setStringList(
            listKey,
            platformEncodedList!,
            options,
          );
          expect(await preferences.getStringList(listKey, options), testList);
        },
      );

      testWidgets(
        'platform list encoding still functions with getPreferences with $backend',
        (WidgetTester _) async {
          final SharedPreferencesAsyncAndroidOptions options = getOptions(
            useDataStore: useDataStore,
            fileName: 'notDefault',
          );
          final preferences = getPreferences() as SharedPreferencesAsyncAndroid;
          await clearPreferences(preferences, options);
          await Future.wait(<Future<void>>[
            preferences.setString(stringKey, testString, options),
            preferences.setBool(boolKey, testBool, options),
            preferences.setInt(intKey, testInt, options),
            preferences.setDouble(doubleKey, testDouble, options),
          ]);
          final SharedPreferencesPigeonOptions pigeonOptions = preferences
              .convertOptionsToPigeonOptions(options);
          final SharedPreferencesAsyncApi api = preferences.getApiForBackend(
            pigeonOptions,
          );
          await api.setDeprecatedStringList(listKey, testList, pigeonOptions);

          final Map<String, Object> prefs = await preferences.getPreferences(
            const GetPreferencesParameters(filter: PreferencesFilters()),
            options,
          );
          expect(prefs[listKey], testList);
        },
      );

      testWidgets(
        'platform list encoding with getPreferences can be re-added with new encoding without data loss with $backend',
        (WidgetTester _) async {
          final SharedPreferencesAsyncAndroidOptions options = getOptions(
            useDataStore: useDataStore,
            fileName: 'notDefault',
          );
          final preferences = getPreferences() as SharedPreferencesAsyncAndroid;
          await clearPreferences(preferences, options);
          await Future.wait(<Future<void>>[
            preferences.setString(stringKey, testString, options),
            preferences.setBool(boolKey, testBool, options),
            preferences.setInt(intKey, testInt, options),
            preferences.setDouble(doubleKey, testDouble, options),
          ]);
          final SharedPreferencesPigeonOptions pigeonOptions = preferences
              .convertOptionsToPigeonOptions(options);
          final SharedPreferencesAsyncApi api = preferences.getApiForBackend(
            pigeonOptions,
          );
          await api.setDeprecatedStringList(listKey, testList, pigeonOptions);

          final Map<String, Object> prefs = await preferences.getPreferences(
            const GetPreferencesParameters(filter: PreferencesFilters()),
            options,
          );
          await preferences.setStringList(
            listKey,
            (prefs[listKey]! as List<Object?>).cast<String>(),
            options,
          );
          expect(await preferences.getStringList(listKey, options), testList);
        },
      );
    });
  }

  runAsyncTests(true);
  runAsyncTests(false);

  testWidgets('Shared Preferences works with multiple files', (
    WidgetTester _,
  ) async {
    final SharedPreferencesAsyncAndroidOptions options1 = getOptions(
      useDataStore: false,
      fileName: 'file1',
    );
    final SharedPreferencesAsyncAndroidOptions options2 = getOptions(
      useDataStore: false,
      fileName: 'file2',
    );
    final SharedPreferencesAsyncPlatform preferences = getPreferences();
    await clearPreferences(preferences, options1);
    await clearPreferences(preferences, options2);

    await preferences.setInt(intKey, 1, options1);
    await preferences.setInt(intKey, 2, options2);
    expect(await preferences.getInt(intKey, options1), 1);
    expect(await preferences.getInt(intKey, options2), 2);
  });

  testWidgets('Shared Preferences can read default sharedPreferences', (
    WidgetTester _,
  ) async {
    final SharedPreferencesAsyncAndroidOptions options = getOptions(
      useDataStore: false,
    );
    final SharedPreferencesAsyncPlatform preferences = getPreferences();

    expect(
      await preferences.getString(
        'thisStringIsWrittenInTheExampleAppJavaCode',
        options,
      ),
      'testString',
    );
  });

  testWidgets('Shared Preferences can read ints by conversion to long', (
    WidgetTester _,
  ) async {
    final SharedPreferencesAsyncAndroidOptions options = getOptions(
      useDataStore: false,
    );
    final SharedPreferencesAsyncPlatform preferences = getPreferences();

    expect(
      await preferences.getInt(
        'thisIntIsWrittenInTheExampleAppJavaCode',
        options,
      ),
      5,
    );
  });
}
